package com.lagou.sqlSession;

import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.pojo.StatementType;

import java.lang.reflect.*;
import java.util.List;

public class DefauleSqlSession implements SqlSession {
    private Configuration configuration;

    public DefauleSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <E> List<E> selectList(String statementid, Object... params) throws Exception {
        simpleExecutor simpleExecutor = new simpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementid);
        List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);
        return (List<E>) list;
    }

    @Override
    public <T> T selectOne(String statementid, Object... params) throws Exception {
        List<Object> objects = selectList(statementid, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        } else {
            throw new RuntimeException("查询结果为空或返回结果过多");
        }
    }

    @Override
    public void insert(String statementid, Object params) throws Exception {
        executeUpdate(statementid, params);
    }

    @Override
    public void update(String statementid, Object params) throws Exception {
        executeUpdate(statementid,params);
    }

    @Override
    public void deleteById(String statementid, Object params) throws Exception {
        executeUpdate(statementid, params);
    }

    public void executeUpdate(String statementid, Object params) throws Exception {
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementid);
        simpleExecutor simpleExecutor = new simpleExecutor();
        simpleExecutor.update(configuration, mappedStatement, params);
    }

    @Override
    public <T> T getMapper(Class<T> clazz) {
        //使用JDK动态代理来为Dao接口生成代理对象，并返回
        Object proxyInstance = Proxy.newProxyInstance(DefauleSqlSession.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 底层都还是去执行JDBC代码 //根据不同情况，来调用selctList或者selectOne
                // 准备参数 1：statmentid :sql语句的唯一标识：namespace.id= 接口全限定名.方法名
                // 方法名：findAll(id)
                String methodName = method.getName();
                //接口全限定名(namespace)
                String className = method.getDeclaringClass().getName();
                String statementId = className + "." + methodName;
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                StatementType statementType = mappedStatement.getStatementType();

                //根据不同类型的语句，选择不同的方法
                switch (statementType) {
                    case Select:
                        return select(method, args, statementId);
                    case Insert:
                        insert(statementId, args[0]);
                        return null;
                    case Update:
                        update(statementId,args[0]);
                        return null;
                    case Delete:
                        deleteById(statementId,args[0]);
                        return null;
                    default:
                        throw new IllegalStateException(
                                String.format("unknown statement type %s in %s", mappedStatement.getStatementType(),
                                        mappedStatement)
                        );
                }
            }
        });
        return (T) proxyInstance;
    }

    private Object select(Method method, Object[] args, String statementId) throws Exception {
        // 准备参数2：params:args
        // 获取被调用方法的返回值类型
        Type genericReturnType = method.getGenericReturnType();
        // 判断是否进行了 泛型类型参数化
        if (genericReturnType instanceof ParameterizedType) {
            return selectList(statementId, args);
        }
        return selectOne(statementId, args);
    }
}
